﻿using System.Collections.Generic;
using System.Globalization;
using System;
using System.Resources;
using System.IO;
using System.Linq;
using System.Text;
using Localization.Repository.Models;
using System.Data.SqlServerCe;

namespace Localization.Repository
{
    public class ResourceRepository : IDisposable
    {
        ResourceData dbContext;

        public ResourceRepository()
        {
            dbContext = new ResourceData();
        }

        public ResourceRepository(string fileName)
        {
            string connection = "Data Source=" + fileName + ";Persist Security Info=False;";

            //// see if file exists, if not we need to create sql ce file
            //if (!File.Exists(fileName))
            //{
            //    SqlCeEngine engine = new SqlCeEngine(connection);
            //    engine.CreateDatabase();
            //    engine.Dispose();
            //}

            // make connections string           
            dbContext = new ResourceData(connection, false);
        }

        public ResourceRepository(string folder, string fileName)
            : this(string.Format("{0}\\{1}.sdf", folder, fileName))
        {          
        }


        public void Dispose()
        {
            if (dbContext != null)
                dbContext.Dispose();
        }

        public IEnumerable<ResourceClassKey> GetClassKeys()
        {
            var list = from ck in dbContext.ClassKeys
                       select ck;
            return list.ToList();
        }

        public IEnumerable<ResourceCulture> GetCultures()
        {
            var list = from c in dbContext.Cutlures
                       select c;
            return list.ToList();
        }

        public IEnumerable<ResourceValue> SearchResourceText(string searchText, bool searchBeginsWith, string classKeyName, CultureInfo culture)
        {
            var query = from rv in dbContext.ResourceValues
                        where rv.Culture.Name == culture.Name
                        select rv;


            if (!string.IsNullOrEmpty(classKeyName))
            {
                query = from rv in query
                        join k in dbContext.ResourceKeys
                        on rv.ResourceKeyID equals k.ResourceKeyID
                        join ck in dbContext.ClassKeys
                        on k.ResourceClassKeyID equals ck.ResourceClassKeyID
                        where ck.ClassKeyName.Equals(classKeyName, StringComparison.InvariantCultureIgnoreCase)
                        select rv;
            }

            //if (translated.HasValue)
            //    criteria.Add(Expression.Eq("IsTranslated", translated));

            if (!string.IsNullOrEmpty(searchText))
            {
                if (searchBeginsWith)
                    query = from rv in query
                            where rv.Value.StartsWith(searchText)
                            select rv;
                else
                    query = from rv in query
                            where rv.Value.Contains(searchText)
                            select rv;
            }

            return query.ToList();
        }

        public ResourceCulture GetDefaultCulture()
        {
            var culture = (from c in dbContext.Cutlures
                           where c.IsDefault
                           select c).FirstOrDefault();
            return culture;
        }

        public void AddNewCulture(CultureInfo culture, bool isDefault)
        {
            ResourceCulture newCulture = new ResourceCulture
            {
                Name = culture.Name,
                IsDefault = isDefault
            };

            // get current default
            ResourceCulture currentDefault = GetDefaultCulture();

            if (newCulture.IsDefault &&
                currentDefault != null &&
                newCulture.Name != currentDefault.Name)
            {
                currentDefault.IsDefault = false;
                dbContext.Cutlures.Attach(currentDefault);
            }

            dbContext.Cutlures.Add(newCulture);
            dbContext.SaveChanges();

            // create a list of resources to add for new culture value
            var query = from r in dbContext.ResourceValues
                        join c in dbContext.Cutlures
                        on r.ResourceCultureID equals c.ResourceCultureID
                        where c.IsDefault
                        select r;

            foreach (var item in query)
            {
                item.Culture = newCulture;
                dbContext.ResourceValues.Add(item);
            }

            dbContext.SaveChanges();
        }

        public void AddResource(string name, string value, string classKeyName, string comment)
        {
            ResourceClassKey ck = GetClassKeyByName(classKeyName);
            ResourceKey rk = new ResourceKey
            {
                Name = name,
                ClassKey = ck,
                Comment = comment
            };

            // save resource key
            dbContext.ResourceKeys.Add(rk);

            foreach (ResourceCulture rc in GetCultures())
            {
                ResourceValue rv = new ResourceValue
                {
                    ResourceKey = rk,
                    Culture = rc,
                    Value = value
                };

                dbContext.ResourceValues.Add(rv);
            }

            dbContext.SaveChanges();
        }

        public ResourceClassKey GetClassKeyByName(string classKeyName)
        {
            if (string.IsNullOrEmpty(classKeyName))
                throw new ArgumentNullException("classKeyName", "Class Key cannot be empty or null.");

            var classKey = (from ck in dbContext.ClassKeys
                            where string.Compare(ck.ClassKeyName, classKeyName, true) == 0
                            select ck).FirstOrDefault();
            return classKey;
        }

        public void AddClassKey(ResourceClassKey classKey)
        {
            if (ResourceFileHelper.ValidateResourceClassKeyName(classKey.ClassKeyName))
            {
                dbContext.ClassKeys.Add(classKey);
                dbContext.SaveChanges();
            }
            else
                throw new ArgumentException("Class key name cannot be empty and must be a word with no breaks.", "classKey");
        }

        public ResourceValue GetDefaultResource(string name, string classKeyName)
        {
            var value = (from rv in dbContext.ResourceValues
                         join r in dbContext.ResourceKeys
                         on rv.ResourceKeyID equals r.ResourceKeyID
                         join k in dbContext.ClassKeys
                         on r.ResourceClassKeyID equals k.ResourceClassKeyID
                         where rv.Culture.IsDefault
                         && classKeyName.Equals(k.ClassKeyName, StringComparison.InvariantCultureIgnoreCase)
                         && name.Equals(r.Name, StringComparison.InvariantCultureIgnoreCase)
                         select rv)
                        .FirstOrDefault();
            return value;
        }

        public ResourceValue GetResource(string name, string classKeyName, CultureInfo culture)
        {
            var value = (from rv in dbContext.ResourceValues
                         join r in dbContext.ResourceKeys
                         on rv.ResourceKeyID equals r.ResourceKeyID
                         join k in dbContext.ClassKeys
                         on r.ResourceClassKeyID equals k.ResourceClassKeyID
                         where rv.Culture.Name == culture.Name
                         && classKeyName.Equals(k.ClassKeyName, StringComparison.InvariantCultureIgnoreCase)
                         && name.Equals(r.Name, StringComparison.InvariantCultureIgnoreCase)
                         select rv)
                         .FirstOrDefault();
            return value;
        }

        public void ModifyDefaultCulture(string name, string value, string comment, string classKeyName)
        {
            if (string.IsNullOrEmpty(value))
                throw new ArgumentNullException("value");
            if (string.IsNullOrEmpty(classKeyName))
                throw new ArgumentNullException("classKeyName");

            // get default culture 
            ResourceValue defaultResource = GetDefaultResource(name, classKeyName);
            ResourceClassKey classKey = GetClassKeyByName(classKeyName);

            defaultResource.ResourceKey.ClassKey = classKey;
            defaultResource.Value = value;
            defaultResource.ResourceKey.Comment = comment;

            dbContext.ResourceValues.Attach(defaultResource);
            dbContext.ClassKeys.Attach(classKey);

            dbContext.SaveChanges();

            // modify all existing resources with this
            // resource ID that is not the default culture
            var resources = from rv in dbContext.ResourceValues
                            join r in dbContext.ResourceKeys
                            on rv.ResourceKeyID equals r.ResourceKeyID
                            join k in dbContext.ClassKeys
                            on r.ResourceClassKeyID equals k.ResourceClassKeyID
                            where classKeyName.Equals(k.ClassKeyName, StringComparison.InvariantCultureIgnoreCase)
                            && name.Equals(r.Name, StringComparison.InvariantCultureIgnoreCase)
                            select rv;

            foreach (ResourceValue r in resources)
                if (r.Culture.Name != defaultResource.Culture.Name)
                {
                    r.ResourceKey = defaultResource.ResourceKey;
                    r.Value = defaultResource.Value;
                    r.IsTranslated = false; // mark as not translated anymore
                    dbContext.ResourceValues.Attach(r);
                }

            // commit changes
            dbContext.SaveChanges();
        }

        public void Translate(string name, string value, string classKeyName, CultureInfo culture)
        {
            if (culture == null)
                throw new ArgumentNullException("culture");
            if (string.IsNullOrEmpty(value))
                throw new ArgumentNullException("value");
            if (string.IsNullOrEmpty(classKeyName))
                throw new ArgumentNullException("classKeyName");

            // get resouce to edit
            ResourceValue resource = GetResource(name, classKeyName, culture);
            if (resource == null)
                throw new ArgumentNullException(string.Format(
                    "Cannot find resource value for culture [{0}] and resource name [{1}].",
                    culture.Name,
                    name));

            // check that this is not the default culutre
            ResourceCulture defaultCulture = GetDefaultCulture();
            if (resource.Culture.Name == defaultCulture.Name)
                throw new ArgumentException(string.Format("Cannot translate default culutre.  Culture: [{0}]." + culture.Name));

            // translate value
            resource.Value = value;
            resource.IsTranslated = true;
            dbContext.ResourceValues.Attach(resource);

            // commit changes
            dbContext.SaveChanges();
        }

        public IEnumerable<ResourceValue> UnTranslatedValues(string classKeyName, CultureInfo culture)
        {
            return null;
            //using (ISession session = NHibernateSessionHelper.OpenSession())
            //using (session.BeginTransaction())
            //{
            //    ICriteria criteria = session.CreateCriteria(typeof(ResourceValue))
            //        .CreateAlias("Culture", "c", NHibernate.SqlCommand.JoinType.InnerJoin)
            //        .CreateAlias("ResourceKey", "r", NHibernate.SqlCommand.JoinType.InnerJoin)
            //        .CreateAlias("r.ClassKey", "k", NHibernate.SqlCommand.JoinType.InnerJoin)
            //        .Add(Expression.Eq("k.ClassKeyName", classKeyName))
            //        .Add(Expression.Eq("c.Name", culture.Name))
            //        .Add(Expression.Eq("IsTranslated", false));

            //    return criteria.List<ResourceValue>();
            //}
        }

        public ResourceClassKey ModifyClassKey(ResourceClassKey classKey)
        {
            var key = (from k in dbContext.ClassKeys
                       where k.ResourceClassKeyID == classKey.ResourceClassKeyID
                       select k).FirstOrDefault();

            if (key == null)
                return null;

            dbContext.ClassKeys.Attach(classKey);
            dbContext.SaveChanges();

            return classKey;
        }

        public void RemoveResourceKey(string name, string classKeyName)
        {
            var resources = from rv in dbContext.ResourceValues
                            join r in dbContext.ResourceKeys
                            on rv.ResourceKeyID equals r.ResourceKeyID
                            join k in dbContext.ClassKeys
                            on r.ResourceClassKeyID equals k.ResourceClassKeyID
                            where classKeyName.Equals(k.ClassKeyName, StringComparison.InvariantCultureIgnoreCase)
                            && name.Equals(r.Name, StringComparison.InvariantCultureIgnoreCase)
                            select rv;

            foreach (var item in resources)
            {
                // remove resource value
                dbContext.ResourceValues.Remove(item);
                // remove resource key
                dbContext.ResourceKeys.Remove(item.ResourceKey);
            }

            dbContext.SaveChanges();
        }

        public void GenereateResourceFiles(string classKey, string outputFolder, ResourceFileType fileType)
        {
            // see if dir exists
            if (!Directory.Exists(outputFolder))
                Directory.CreateDirectory(outputFolder);

            StringBuilder batchFileText = new StringBuilder();
            batchFileText.AppendLine("@echo off");
            string runtimeDir = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
            batchFileText.AppendLine(
                "set al_exe=\""
                + runtimeDir.Substring(0, runtimeDir.LastIndexOf("v")) + "v2.0.50727"
                + "\\al.exe\"");

            // cycle each class key
            ResourceClassKey ck = GetClassKeyByName(classKey);
            if (ck == null)
                throw new ArgumentNullException("Class key could not be resolved.");

            // look through each culture and create assembly
            foreach (ResourceCulture culture in GetCultures())
            {
                string fileName = null;
                string currentFolder = null;

                switch (fileType)
                {
                    case ResourceFileType.DotNet_Resource:
                        if (culture.IsDefault)
                        {
                            fileName = string.Format("{0}.resources", ck.ClassKeyName);
                            currentFolder = outputFolder;
                        }
                        else
                        {
                            fileName = string.Format("{0}.{1}.resources", ck.ClassKeyName, culture.Name);
                            currentFolder = string.Format("{0}\\{1}", outputFolder, culture.Name);
                            if (!Directory.Exists(currentFolder))
                                Directory.CreateDirectory(currentFolder);
                        }
                        break;
                    case ResourceFileType.DotNet_ResX:
                        if (culture.IsDefault)
                        {
                            fileName = string.Format("{0}.resx", ck.ClassKeyName);
                            currentFolder = outputFolder;
                        }
                        else
                        {
                            fileName = string.Format("{0}.{1}.resx", ck.ClassKeyName, culture.Name);
                            currentFolder = outputFolder;
                            if (!Directory.Exists(currentFolder))
                                Directory.CreateDirectory(currentFolder);
                        }
                        break;

                }

                IEnumerable<ResourceValue> resources =
                    SearchResourceText(null, true, ck.ClassKeyName, culture.GetCultureFromName());

                // generate files
                ResourceFileHelper.GenerateResourceFile(fileType, currentFolder, fileName, resources);

                // create batch to run al.exe
                if (culture.IsDefault)
                    batchFileText.AppendLine(
                        string.Format("%al_exe% /t:lib /embed:{0} /culture:{1} /out:{2}.resources.dll",
                        fileName,
                        culture.Name,
                        ck.ClassKeyName));
                else
                    batchFileText.AppendLine(
                        string.Format("%al_exe% /t:lib /embed:{1}\\{0} /culture:{1} /out:{1}\\{2}.resources.dll",
                        fileName,
                        culture.Name,
                        ck.ClassKeyName));
            }

            // write batch file
            System.IO.File.WriteAllText(outputFolder + "\\generate_satellites.bat", batchFileText.ToString());
        }
    }
}
